/********************************Copyright (c)**********************************\
**
 **                   (c) Copyright 2023, Main, China, QD.
 **                           All Rights Reserved
 **
 **                                 By(wo4fisher)
 **                           http://www.wo4fisher.com
 **
 **----------------------------------文件信息------------------------------------
 ** 文件名称: myiic.c
 ** 创建人员: wht
 ** 创建日期: 2023-12-19
 ** 文档描述:
 **
 **----------------------------------版本信息------------------------------------
 ** 版本代号: V0.1
 ** 版本说明: 初始版本
 **
 **------------------------------------------------------------------------------
 \********************************End of Head************************************/
#include "myiic.h"

#define IIC_DELAY_TIME 10
#define IIC_WAIT_ACK_TIME 200

/**
 * @brief IIC延时
 * @param  无
 * @return 无
 */
void __IIC_Delayus(volatile uint16_t time) {
    uint16_t i = time, j; // 用户根据自己的MCU时钟进行设置倍数

    while ( i-- )
    {
        for(j=24;j>0;j--)
        __NOP();
    }
}

/**
 * @brief IIC初始化
 * @param  无
 * @return 无
 */
void Soft_IIC_Init(void) {
    GPIO_InitTypeDef SOFT_IIC_GPIO_STRUCT = { 0 };

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

    SOFT_IIC_GPIO_STRUCT.GPIO_Pin = IIC_SDA_PIN | IIC_SCL_PIN;
    SOFT_IIC_GPIO_STRUCT.GPIO_Mode = GPIO_Mode_Out_PP;
    SOFT_IIC_GPIO_STRUCT.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &SOFT_IIC_GPIO_STRUCT);

    IIC_SDA_H();
    IIC_SCL_H();
}

/**
 * @brief SDA引脚设置输出模式
 * @param  无
 * @return 无
 */
static void Soft_IIC_Output(void) {
    GPIO_InitTypeDef SOFT_IIC_GPIO_STRUCT;
    SOFT_IIC_GPIO_STRUCT.GPIO_Mode = GPIO_Mode_Out_PP;
    SOFT_IIC_GPIO_STRUCT.GPIO_Pin = IIC_SDA_PIN;
    SOFT_IIC_GPIO_STRUCT.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(IIC_SDA_PORT, &SOFT_IIC_GPIO_STRUCT);
}

/**
 * @brief SDA引脚设置输入模式
 * @param  无
 * @return 无
 */
static void Soft_IIC_Input(void) {
    GPIO_InitTypeDef SOFT_IIC_GPIO_STRUCT;
    SOFT_IIC_GPIO_STRUCT.GPIO_Mode = GPIO_Mode_IPU;
    SOFT_IIC_GPIO_STRUCT.GPIO_Pin = IIC_SDA_PIN;
    SOFT_IIC_GPIO_STRUCT.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(IIC_SDA_PORT, &SOFT_IIC_GPIO_STRUCT);
}

/**
 * @brief IIC起始信号
 * @param  无
 * @return 无
 * SCL  ¯¯¯¯¯\_______
 * SDA  ¯¯¯\_________
 */
void Soft_IIC_Start(void) {
    Soft_IIC_Output();
//    IIC_SCL_L();
    IIC_SDA_H();
    IIC_SCL_H();
    __IIC_Delayus( IIC_DELAY_TIME);
    IIC_SDA_L();
    __IIC_Delayus( IIC_DELAY_TIME);
    IIC_SCL_L();
}

/**
 * @brief IIC停止信号
 * @param  无
 * @return 无
 * SCL  _____/¯¯¯¯¯¯¯
 * SDA  _______/¯¯¯¯¯
 */
void Soft_IIC_Stop(void) {
    Soft_IIC_Output();
    IIC_SCL_L();
    IIC_SDA_L();
    IIC_SCL_H();
    __IIC_Delayus(IIC_DELAY_TIME);
    IIC_SDA_H();
    __IIC_Delayus(IIC_DELAY_TIME);
}

/**
 * @brief IIC应答信号
 * @param  无
 * @return 无
 * SCL  _____/¯¯¯\_____
 *
 * SDA  _______________
 */
void Soft_IIC_ACK(void) {
    IIC_SCL_L();
    Soft_IIC_Output();
    __IIC_Delayus( IIC_DELAY_TIME);
    IIC_SDA_L();
    __IIC_Delayus( IIC_DELAY_TIME);
    IIC_SCL_H();
    __IIC_Delayus( IIC_DELAY_TIME);
    IIC_SCL_L();
}

/**
 * @brief IIC无应答信号
 * @param  无
 * @return 无
 * SCL  _____/¯¯¯\_____
 *
 * SDA  ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
 */
void Soft_IIC_NACK(void) {
    IIC_SCL_L();
    Soft_IIC_Output();
    __IIC_Delayus( IIC_DELAY_TIME);
    IIC_SDA_H();
    __IIC_Delayus( IIC_DELAY_TIME);
    IIC_SCL_H();
    __IIC_Delayus( IIC_DELAY_TIME);
    IIC_SCL_L();
}

/**
 * @brief IIC等待应答信号
 * @param  无
 * @return 0无应答  1有应答
 * 当检测到start信号后，在随后的8个时钟周期，
 * SDA线进行一次8bit的数据传输，若接收到相应的8bit信号，
 * 则在第9个时钟周期拉低SDA信号，并视为一次ack应答信号
 */
uint8_t Soft_IIC_Wait_ACK(void) {
    uint16_t wait = 0;
//    Soft_IIC_Output();//释放SDA
//    IIC_SDA_H();      //释放SDA
    Soft_IIC_Input(); //转为输入
    IIC_SCL_H();      //释放SCL
    __IIC_Delayus( IIC_DELAY_TIME);
    while ( GPIO_ReadInputDataBit( IIC_SDA_PORT, IIC_SDA_PIN ) == Bit_SET )
    {
        wait++;       //加入超时
        if ( wait > IIC_WAIT_ACK_TIME )
        {
            Soft_IIC_Stop();       //是否有必要停？
            return 0;
        }
    }
    IIC_SCL_L();
    return 1;
}

/**
 * @brief IIC写数据1
 * @param  无
 * @return 无
 */
void Soft_IIC_Write_High(void) {
    IIC_SCL_L();
    IIC_SDA_H();
    __IIC_Delayus( IIC_DELAY_TIME);
    IIC_SCL_H();
    __IIC_Delayus( IIC_DELAY_TIME);
    IIC_SCL_L();
//      __IIC_Delayus ( IIC_DELAY_TIME );
}

/**
 * @brief IIC写数据0
 * @param  无
 * @return 无
 */
void Soft_IIC_Write_Low(void) {
    IIC_SCL_L();
    IIC_SDA_L();
    __IIC_Delayus( IIC_DELAY_TIME);
    IIC_SCL_H();
    __IIC_Delayus( IIC_DELAY_TIME);
    IIC_SCL_L();
    __IIC_Delayus( IIC_DELAY_TIME);
}

/**
 * @brief IIC写入单个数据
 * @param  无
 * @return 应答信号, 0无应答 1有应答
 * S + (devAddr+0) + ACK + DATA + ACK ... DATA + ACK/NACK + P
 */
uint8_t Soft_IIC_Write_Byte(uint8_t Byte) {
    uint8_t i;
    Soft_IIC_Output();
    for (i = 0x80; i != 0; i >>= 1) {
        if (Byte & i) {
            Soft_IIC_Write_High();
        } else {
            Soft_IIC_Write_Low();
        }
    }
    return (Soft_IIC_Wait_ACK());
}

/**
 * @brief IIC读一个数据
 * @param  ACK:应答 NACK:不应答
 * @return 返回读到的数据
 * S + (devAddr+1) + ACK + DATA + ACK ... DATA + ACK/NACK + P
 */
uint8_t Soft_IIC_Recv_Byte(ACK_STATUS ack_sta) {
    uint8_t data = 0, i;
    Soft_IIC_Input();
    for (i = 0x80; i != 0; i >>= 1)       //高位在前
            {
        IIC_SCL_L();
        __IIC_Delayus( IIC_DELAY_TIME);
        IIC_SCL_H();
        __IIC_Delayus( IIC_DELAY_TIME);
        if (GPIO_ReadInputDataBit( IIC_SDA_PORT, IIC_SDA_PIN) == Bit_SET) {
            data |= i;
        }
        __IIC_Delayus( IIC_DELAY_TIME);
    }
    if (ack_sta == ACK) {
        Soft_IIC_ACK();
    } else {
        Soft_IIC_NACK();
    }
    return data;
}

/**
 * @brief  IIC读多个数据
 * @param  ACK:发送应答 NACK:不应答
 * @return 返回应答信号
 * S + (devAddr+1) + ACK + DATA + ACK ... DATA + ACK/NACK + P
 */
uint8_t Soft_IIC_Mem_Read(uint8_t devAddr, uint8_t memAddr, uint8_t * pData,
        uint8_t len) {
//    uint8_t data = 0, i;
//    Soft_IIC_Input();
//    IIC_SCL_H();
//    __IIC_Delayus ( IIC_DELAY_TIME );
    // 1. start
    Soft_IIC_Start();
    // 2. write devaddr，after write auto wait ack,return ack status
    if (Soft_IIC_Write_Byte(devAddr) == 0)
        return 0;
    // 3. write memaddr，after write auto wait ack,return ack status
    if (Soft_IIC_Write_Byte(memAddr) == 0)
        return 0;
    // 4. start repeated
    Soft_IIC_Start();
    // 5. read devaddr
    if (Soft_IIC_Write_Byte(devAddr | 0x01) == 0)
        return 0;
    // 6. receive data bytes
    while ( len>0 )
    {
        if ( 1 == len )
        * pData = Soft_IIC_Recv_Byte ( NACK ); //接受数据后，发送应答
        else
        * pData = Soft_IIC_Recv_Byte ( ACK );//接受数据后，发送应答
        pData ++;// 地址+1
        len--;// 长度-1
    }
    // 6. stop
    Soft_IIC_Stop();
    return 1;
}

/**
 * @brief IIC写多个数据
 * @param  ACK:应答 NACK:不应答
 * @return 返回读到的数据
 */
uint8_t Soft_IIC_Mem_Write(uint8_t devAddr, uint8_t memAddr, uint8_t * pData,
        uint8_t len) {
//    uint8_t data = 0, i;
//    Soft_IIC_Input();

    // 1. start
    Soft_IIC_Start();
    // 2. write devaddr，after write auto wait ack,return ack status
    if (Soft_IIC_Write_Byte(devAddr) == 0)
        return 0;
    // 3. write memaddr，after write auto wait ack,return ack status
    if (Soft_IIC_Write_Byte(memAddr) == 0)
        return 0;
    // 4. send data
    while ( len>0 )
    {
        Soft_IIC_Write_Byte ( *pData++ ); //依次写数据，等待应答 ？？？？没有判断应答 TODO
        len--;
    }
    // 5. stop
    Soft_IIC_Stop();
    return 1; //success
}
/********************************End of File************************************/
